package com.tpv.tv.lib.pdf;

import com.artifex.mupdf.fitz.SeekableInputStream;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
import android.provider.OpenableColumns;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.PasswordTransformationMethod;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.PopupMenu;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewAnimator;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Locale;

public class DocumentActivity extends Activity {

    /* The core rendering instance */
    enum TopBarMode {Main, Search, More}
    enum SuitableMode {VER, HOR, UNKNOWN}

    private static final Pair<Integer, Integer>[] TURN_PAGE_AUTO_TIME = new Pair[] {
            Pair.create(0, R.drawable.button_off), Pair.create(5, R.drawable.button_5s), Pair.create(10, R.drawable.button_10s),
            Pair.create(15, R.drawable.button_15s), Pair.create(20, R.drawable.button_20s)
    };
    private PdfPageBottomBarSelectedHelper mPdfPageBottomBarFocusHelper;

    private int currentTurnPageAutoIndex = 0; // 关闭状态

    private final int OUTLINE_REQUEST = 0;
    private MuPDFCore core;
    private String mDocTitle;
    private String mDocKey;
    private ReaderView mDocView;
    private WeakReference<ReaderView> mWeakDocView;
    private View mButtonsView;
    private boolean mButtonsVisible;
    private EditText mPasswordView;
    private TextView mDocNameView;
    private ViewGroup mBottomBarView;
    private SeekBar mPageSlider;
    private SeekBar mRightSideSeekBar;
    private int mPageSliderRes;

    private ViewGroup mPageStartBtnContain;
    private View mPageStartBtn;
    private ViewGroup mPagePreviousBtnContain;
    private TextView mPagePreviousBtn;
    private ViewGroup mPageEndBtnContain;
    private TextView mPageEndBtn;
    private ViewGroup mPageNextBtnContain;
    private TextView mPageNextBtn;
    private TextView mPageNumberView;
    private ViewGroup mSuitableBtnContain;
    private View mSuitableBtn;
    private ViewGroup mTurnPageAutoBtnContain;
    private TextView mTurnPageAutoBtn;
    private ImageButton mSearchButton;
    private ImageButton mOutlineButton;
    private ViewAnimator mTopBarSwitcher;
    private ImageButton mLinkButton;
    private TopBarMode mTopBarMode = TopBarMode.Main;
    private ImageButton mSearchBack;
    private ImageButton mSearchFwd;
    private ImageButton mSearchClose;
    private EditText mSearchText;
    private SearchTask mSearchTask;
    private AlertDialog.Builder mAlertBuilder;
    private boolean mLinkHighlight = false;
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private ArrayList<OutlineActivity.Item> mFlatOutline;
    private boolean mReturnToLibraryActivity = false;
    private String mDefaultSuitablePage = SuitableMode.UNKNOWN.name();
    protected int mDisplayDPI;
    private   int mLayoutEM = 10;
    private   int mLayoutW = 312;
    private   int mLayoutH = 504;

    protected View mLayoutButton;
    protected PopupMenu mLayoutPopupMenu;

    private String toHex(byte[] digest) {
        StringBuilder builder = new StringBuilder(2 * digest.length);
        for (byte b : digest)
            builder.append(String.format("%02x", b));
        return builder.toString();
    }

    private MuPDFCore openBuffer(byte[] buffer, String magic) {
        try {
            core = new MuPDFCore(buffer, magic);
        } catch (Exception e) {
            PdfLogUtil.logD("Error opening document buffer: " + e);
            return null;
        }
        return core;
    }

    private MuPDFCore openStream(SeekableInputStream stm, String magic) {
        try {
            core = new MuPDFCore(stm, magic);
        } catch (Exception e) {
            PdfLogUtil.logD("Error opening document stream: " + e);
            return null;
        }
        return core;
    }

    private MuPDFCore openCore(Uri uri, long size, String mimetype) throws IOException {
        ContentResolver cr = getContentResolver();

        PdfLogUtil.logD("Opening document " + uri);

        InputStream is = cr.openInputStream(uri);
        byte[] buf = null;
        int used = -1;
        try {
            final int limit = 8 * 1024 * 1024;
            if (size < 0) { // size is unknown
                buf = new byte[limit];
                assert is != null;
                used = is.read(buf);
                boolean atEOF = is.read() == -1;
                if (used < 0 || (used == limit && !atEOF)) // no or partial data
                    buf = null;
            } else if (size <= limit) { // size is known and below limit
                buf = new byte[(int) size];
                assert is != null;
                used = is.read(buf);
                if (used < 0 || used < size) // no or partial data
                    buf = null;
            }
            if (buf != null && buf.length != used) {
                byte[] newBuf = new byte[used];
                System.arraycopy(buf, 0, newBuf, 0, used);
                buf = newBuf;
            }
        } catch (OutOfMemoryError e) {
            buf = null;
        } finally {
            if(is != null) {
                try {
                    is.close();
                } catch (Exception e) {
                    PdfLogUtil.logD("is.close :" + e.getMessage());
                }
            }
        }

        if (buf != null) {
            PdfLogUtil.logD("  Opening document from memory buffer of size " + buf.length);
            return openBuffer(buf, mimetype);
        } else {
            PdfLogUtil.logD("  Opening document from stream");
            return openStream(new ContentInputStream(cr, uri, size), mimetype);
        }
    }

    private void showCannotOpenDialog(String reason) {
        Log.e("zlq", "open fail reason : " + reason);
        Resources res = getResources();
        AlertDialog alert = mAlertBuilder.create();
        alert.setTitle(String.format(Locale.ROOT, res.getString(R.string.cannot_open_document_Reason), reason));
        alert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.dismiss),
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        finish();
                    }
                });
        alert.show();
    }

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        mDisplayDPI = metrics.densityDpi;

        mAlertBuilder = new AlertDialog.Builder(this, R.style.PdfPwdAlertDialog);

        if (core == null) {
            if (savedInstanceState != null && savedInstanceState.containsKey("DocTitle")) {
                mDocTitle = savedInstanceState.getString("DocTitle");
            }

            Intent intent = getIntent();

            mReturnToLibraryActivity = intent.getIntExtra(getComponentName().getPackageName() + ".ReturnToLibraryActivity", 0) != 0;

            if (Intent.ACTION_VIEW.equals(intent.getAction())) {
                Uri uri = intent.getData();
                String mimetype = getIntent().getType();

                if (uri == null) {
                    showCannotOpenDialog("No document uri to open");
                    return;
                }

                mDocKey = uri.toString();

                PdfLogUtil.logD("OPEN URI: " + uri);
                PdfLogUtil.logD("MAGIC (Intent): " + mimetype);

                mDocTitle = null;
                long size = -1;

                try (Cursor cursor = getContentResolver().query(uri, null, null, null, null)) {
                    PdfLogUtil.logD("query -- prepare");
                    if (cursor != null && cursor.moveToFirst()) {
                        int idx;
                        PdfLogUtil.logD("query -- start");
                        idx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                        if (idx >= 0 && cursor.getType(idx) == Cursor.FIELD_TYPE_STRING)
                            mDocTitle = cursor.getString(idx);

                        idx = cursor.getColumnIndex(OpenableColumns.SIZE);
                        if (idx >= 0 && cursor.getType(idx) == Cursor.FIELD_TYPE_INTEGER)
                            size = cursor.getLong(idx);

                        if (size == 0)
                            size = -1;
                    }
                }

                PdfLogUtil.logD("  NAME " + mDocTitle);
                PdfLogUtil.logD("  SIZE " + size);

                if (mimetype == null || mimetype.equals("application/octet-stream")) {
                    mimetype = getContentResolver().getType(uri);
                    PdfLogUtil.logD("  MAGIC (Resolved) " + mimetype);
                }
                if (mimetype == null || mimetype.equals("application/octet-stream")) {
                    mimetype = mDocTitle;
                    PdfLogUtil.logD("  MAGIC (Filename) " + mimetype);
                }

                try {
                    core = openCore(uri, size, mimetype);
                    SearchTaskResult.set(null);
                } catch (Exception x) {
                    showCannotOpenDialog(x.toString());
                    return;
                }
            }
            if (core != null && core.needsPassword()) {
                requestPassword(savedInstanceState);
                return;
            }
            if (core != null && core.countPages() == 0) {
                core = null;
            }
        }
        if (core == null) {
            AlertDialog alert = mAlertBuilder.create();
            alert.setTitle(R.string.cannot_open_document);
            alert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.dismiss),
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            finish();
                        }
                    });
            alert.setOnCancelListener(new OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialog) {
                    finish();
                }
            });
            alert.show();

            Button negativeButton = alert.getButton(DialogInterface.BUTTON_POSITIVE);
            negativeButton.setClickable(true);
            negativeButton.setFocusable(true);
            negativeButton.setFocusableInTouchMode(true);
            negativeButton.requestFocus();

            Window window = alert.getWindow();
            if(window != null) {
                window.setBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.pdf_warming_dialog_bg));
            }
            return;
        }
        createUI(savedInstanceState);
    }

    public void requestPassword(final Bundle savedInstanceState) {
        View pdfPwdDialogContent = LayoutInflater.from(this).inflate(R.layout.layout_pdf_pwd_dialog, null);
        AlertDialog alert = mAlertBuilder.create();
        alert.setCanceledOnTouchOutside(false);
        alert.setView(pdfPwdDialogContent);
        alert.show();

        TextView cancelBtn = pdfPwdDialogContent.findViewById(R.id.pwdCancelBtn);
        cancelBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
        TextView okBtn = pdfPwdDialogContent.findViewById(R.id.pwdOkBtn);
        okBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (core.authenticatePassword(mPasswordView.getText().toString())) {
                    alert.dismiss();
                    createUI(savedInstanceState);
                } else {
                    Toast.makeText(DocumentActivity.this, getString(R.string.password_incorrect), Toast.LENGTH_SHORT).show();
                    requestPassword(savedInstanceState);
                }
            }
        });

        ViewGroup etOutline = pdfPwdDialogContent.findViewById(R.id.pwdEditTextOutline);
        mPasswordView = pdfPwdDialogContent.findViewById(R.id.pwdEditText);
        mPasswordView.setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
        mPasswordView.setTransformationMethod(new PasswordTransformationMethod());
        mPasswordView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean focus) {
                etOutline.setBackground(ContextCompat.getDrawable(DocumentActivity.this, focus ? R.drawable.pdf_pwd_dialog_edittext_outline_bg : R.drawable.pdf_pwd_dialog_edittext_outline_no_bg));
            }
        });
        mPasswordView.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

            @Override
            public void afterTextChanged(Editable editable) {
                boolean isOkBtnEnable = !TextUtils.isEmpty(editable.toString());
                okBtn.setEnabled(isOkBtnEnable);
                okBtn.setTextColor(ContextCompat.getColor(DocumentActivity.this, isOkBtnEnable ? android.R.color.white : R.color.color_text_disable));
            }
        });
        mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
                PdfLogUtil.logD("[onEditorAction] actionId : " + actionId);
                // 检查是否是 "完成" 操作
                if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT) {
                    // 处理完成操作
                    PdfCommUtil.closeKeyboard(DocumentActivity.this, mPasswordView);
                    boolean isEmptyInput = TextUtils.isEmpty(mPasswordView.getText().toString());
                    if(isEmptyInput) {
                        cancelBtn.requestFocus();
                    } else {
                        okBtn.requestFocus();
                    }
                    return true; // 返回 true 表示事件已处理
                }
                return false;
            }
        });

        boolean isOkBtnEnable = !TextUtils.isEmpty(mPasswordView.getText().toString());
        okBtn.setEnabled(isOkBtnEnable);
        okBtn.setTextColor(ContextCompat.getColor(this, isOkBtnEnable ? android.R.color.white : R.color.color_text_disable));

        Window window = alert.getWindow();
        if(window != null) {
            window.setBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.pdf_pwd_dialog_bg));
        }
    }

    public void relayoutDocument() {
        int loc = core.layout(mDocView.mCurrent, mLayoutW, mLayoutH, mLayoutEM);
        mFlatOutline = null;
        mDocView.mHistory.clear();
        mDocView.refresh();
        mDocView.setDisplayedViewIndex(loc);
    }

    @SuppressLint("SetTextI18n")
    public void createUI(Bundle savedInstanceState) {
        if (core == null) return;
        // Now create the UI.
        // First create the document view
        mDocView = new ReaderView(this) {
            @Override
            protected void onMoveToChild(int i) {
                if (core == null) return;
                PdfLogUtil.logD("[ReaderView] onMoveToChild index : " + i);
                triggerTurnPageAuto(true);
                mPageNumberView.setText(String.format(Locale.ROOT, "%d/%d", i + 1, core.countPages()));
                mPageSlider.setMax((core.countPages() - 1) * mPageSliderRes);
                mPageSlider.setProgress(i * mPageSliderRes);

                // 只有一页就都置灰
                checkTurnLeftButtons(mDocView.getAdapter().getCount() > 1);
                checkTurnRightButtons(mDocView.getAdapter().getCount() > 1);

                // 更新按钮焦点状态
                updateCtrlBtnFocusState(i, false);

                super.onMoveToChild(i);
            }

            @Override
            protected void onTapMainDocArea() {
                if (!mButtonsVisible) {
                    showButtons();
                } else {
                    if (mTopBarMode == TopBarMode.Main)
                        hideButtons();
                }
            }

            @Override
            protected void onDocMotion() {
                hideButtons();
            }

            @Override
            public void onSizeChanged(int w, int h, int oldw, int oldh) {
                PdfLogUtil.logD("[ReaderView-onSizeChanged] isReflowable : " + core.isReflowable());
                if (core.isReflowable()) {
                    mLayoutW = w * 72 / mDisplayDPI;
                    mLayoutH = h * 72 / mDisplayDPI;
                    relayoutDocument();
                } else {
                    refresh();
                }
            }
        };
        mDocView.setBackwardListener(new ReaderView.PdfScaleMoveListener() {
            @Override
            public void onMove(int total, int page) {
                updateRightSeekbarProgress(total, page);
            }
        });

        mDocView.setForwardListener(new ReaderView.PdfScaleMoveListener() {
            @Override
            public void onMove(int total, int page) {
                updateRightSeekbarProgress(total, page);
            }
        });
        mWeakDocView = new WeakReference<>(mDocView);
        mDocView.setAdapter(new PageAdapter(this, core));

        mSearchTask = new SearchTask(this, core) {
            @Override
            protected void onTextFound(SearchTaskResult result) {
                SearchTaskResult.set(result);
                // Ask the ReaderView to move to the resulting page
                mDocView.setDisplayedViewIndex(result.pageNumber);
                // Make the ReaderView act on the change to SearchTaskResult
                // via overridden onChildSetup method.
                mDocView.resetupChildren();
            }
        };

        // Make the buttons overlay, and store all its
        // controls in variables
        makeButtonsView();

        // Set up the page slider
        int smaX = Math.max(core.countPages() - 1, 1);
        mPageSliderRes = ((10 + smaX - 1) / smaX) * 2;

        // Set the file-name text
        String docTitle = core.getTitle();
        PdfLogUtil.logD("docTitle = " + docTitle + ", mDocTitle = " + mDocTitle);
        if(!TextUtils.isEmpty(mDocTitle)) {
            mDocNameView.setText(mDocTitle);
        } else if(!TextUtils.isEmpty(docTitle)) {
            mDocNameView.setText(docTitle);
        } else {
            mDocNameView.setText("UnKnown File Title");
        }
//        if (docTitle != null)
//            mDocNameView.setText(docTitle);
//        else
//            mDocNameView.setText(mDocTitle);

        // Activate the seekbar
        mPageSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            public void onStopTrackingTouch(SeekBar seekBar) {
                if(mDocView != null) {
                    mDocView.pushHistory();
                    mDocView.setDisplayedViewIndex((seekBar.getProgress() + mPageSliderRes / 2) / mPageSliderRes);
                }
            }

            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            public void onProgressChanged(SeekBar seekBar, int progress,
                                          boolean fromUser) {
                updatePageNumView((progress + mPageSliderRes / 2) / mPageSliderRes);
            }
        });

        // Activate the search-preparing button
        mSearchButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                searchModeOn();
            }
        });

        mSearchClose.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                searchModeOff();
            }
        });

        // Search invoking buttons are disabled while there is no text specified
        mSearchBack.setEnabled(false);
        mSearchFwd.setEnabled(false);
        mSearchBack.setColorFilter(Color.argb(255, 128, 128, 128));
        mSearchFwd.setColorFilter(Color.argb(255, 128, 128, 128));

        // React to interaction with the text widget
        mSearchText.addTextChangedListener(new TextWatcher() {

            public void afterTextChanged(Editable s) {
                boolean haveText = !s.toString().isEmpty();
                setButtonEnabled(mSearchBack, haveText);
                setButtonEnabled(mSearchFwd, haveText);

                // Remove any previous search results
                if (SearchTaskResult.get() != null && !mSearchText.getText().toString().equals(SearchTaskResult.get().txt)) {
                    SearchTaskResult.set(null);
                    mDocView.resetupChildren();
                }
            }

            public void beforeTextChanged(CharSequence s, int start, int count,
                                          int after) {
            }

            public void onTextChanged(CharSequence s, int start, int before,
                                      int count) {
            }
        });

        //React to Done button on keyboard
        mSearchText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_DONE)
                    search(1);
                return false;
            }
        });

        mSearchText.setOnKeyListener(new View.OnKeyListener() {
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER)
                    search(1);
                return false;
            }
        });

        // Activate search invoking buttons
        mSearchBack.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                search(-1);
            }
        });

        mSearchFwd.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                search(1);
            }
        });

        mLinkButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                setLinkHighlight(!mLinkHighlight);
            }
        });

        if (core.isReflowable()) {
            mLayoutButton.setVisibility(View.VISIBLE);
            mLayoutPopupMenu = new PopupMenu(this, mLayoutButton);
            mLayoutPopupMenu.getMenuInflater().inflate(R.menu.layout_menu, mLayoutPopupMenu.getMenu());
            mLayoutPopupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                public boolean onMenuItemClick(MenuItem item) {
                    float oldLayoutEM = mLayoutEM;
                    int id = item.getItemId();
                    if (id == R.id.action_layout_6pt) mLayoutEM = 6;
                    else if (id == R.id.action_layout_7pt) mLayoutEM = 7;
                    else if (id == R.id.action_layout_8pt) mLayoutEM = 8;
                    else if (id == R.id.action_layout_9pt) mLayoutEM = 9;
                    else if (id == R.id.action_layout_10pt) mLayoutEM = 10;
                    else if (id == R.id.action_layout_11pt) mLayoutEM = 11;
                    else if (id == R.id.action_layout_12pt) mLayoutEM = 12;
                    else if (id == R.id.action_layout_13pt) mLayoutEM = 13;
                    else if (id == R.id.action_layout_14pt) mLayoutEM = 14;
                    else if (id == R.id.action_layout_15pt) mLayoutEM = 15;
                    else if (id == R.id.action_layout_16pt) mLayoutEM = 16;
                    if (oldLayoutEM != mLayoutEM)
                        relayoutDocument();
                    return true;
                }
            });
            mLayoutButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    mLayoutPopupMenu.show();
                }
            });
        }

        if (core.hasOutline()) {
            // 暂时不需要outline能力
            mOutlineButton.setVisibility(View.GONE);
            mOutlineButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    if (mFlatOutline == null)
                        mFlatOutline = core.getOutline();
                    if (mFlatOutline != null) {
                        Intent intent = new Intent(DocumentActivity.this, OutlineActivity.class);
                        Bundle bundle = new Bundle();
                        bundle.putInt("POSITION", mDocView.getDisplayedViewIndex());
                        bundle.putSerializable("OUTLINE", mFlatOutline);
                        intent.putExtra("PALLETBUNDLE", Pallet.sendBundle(bundle));
                        startActivityForResult(intent, OUTLINE_REQUEST);
                    }
                }
            });
        } else {
            mOutlineButton.setVisibility(View.GONE);
        }

//        // 不需要缓存页面
//        SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
//        mDocView.setDisplayedViewIndex(prefs.getInt("page" + mDocKey, 0));

        if (savedInstanceState == null || !savedInstanceState.getBoolean("ButtonsHidden", false))
            showButtons();

        if (savedInstanceState != null && savedInstanceState.getBoolean("SearchMode", false))
            searchModeOn();

        // Stick the document view and the buttons overlay into a parent view
        RelativeLayout layout = new RelativeLayout(this);
        layout.setBackgroundColor(ContextCompat.getColor(this, R.color.color_pdf_bg));
        layout.addView(mDocView);

        View rootView = getLayoutInflater().inflate(R.layout.layout_right_side_seek, layout, true);
        mRightSideSeekBar = rootView.findViewById(R.id.right_seek);
        mRightSideSeekBar.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean hasFocus) {
                PdfLogUtil.logD("[RightSideSeekBar] hasFocus : " + hasFocus);
                // 更新（start、pre、next、end四个按钮enable状态）
                mPdfPageBottomBarFocusHelper.updateItemEnable(R.id.btn_pdf_page_start, mPageStartBtn.isEnabled());
                mPdfPageBottomBarFocusHelper.updateItemEnable(R.id.btn_pdf_page_previous, mPagePreviousBtn.isEnabled());
                mPdfPageBottomBarFocusHelper.updateItemEnable(R.id.btn_pdf_page_next, mPageNextBtn.isEnabled());
                mPdfPageBottomBarFocusHelper.updateItemEnable(R.id.btn_pdf_page_end, mPageEndBtn.isEnabled());
                PdfPageBottomBarSelectedHelper.SelectedIdResWithBothSide selectedIdRes = mPdfPageBottomBarFocusHelper.findWhichOneSelectedIdRes();
                if(hasFocus) {
                    // 计算出selectedIdRes索引，并判断是否可以左右
                    if(selectedIdRes.getLeftSideSelectedIdRes() != PdfPageBottomBarSelectedHelper.NONE_RES_ID) {
                        view.setNextFocusLeftId(selectedIdRes.getLeftSideSelectedIdRes());
                    }
                    if(selectedIdRes.getRightSideSelectedIdRes() != PdfPageBottomBarSelectedHelper.NONE_RES_ID) {
                        view.setNextFocusRightId(selectedIdRes.getRightSideSelectedIdRes());
                    }
                } else {
                    mPdfPageBottomBarFocusHelper.findWhichOneSelectedThenUnSelected();
                }
                mPdfPageBottomBarFocusHelper.notifyIdResSelected(selectedIdRes.getSelectedIdRes(), hasFocus);
            }
        });
        layout.addView(mButtonsView);
        setContentView(layout);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == OUTLINE_REQUEST) {
            if (resultCode >= RESULT_FIRST_USER && mDocView != null) {
                mDocView.pushHistory();
                mDocView.setDisplayedViewIndex(resultCode - RESULT_FIRST_USER);
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);

        if (mDocKey != null && mDocView != null) {
            if (mDocTitle != null)
                outState.putString("DocTitle", mDocTitle);

            // Store current page in the prefs against the file name,
            // so that we can pick it up each time the file is loaded
            // Other info is needed only for screen-orientation change,
            // so it can go in the bundle
            SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
            SharedPreferences.Editor edit = prefs.edit();
            edit.putInt("page" + mDocKey, mDocView.getDisplayedViewIndex());
            edit.apply();
        }

        if (!mButtonsVisible)
            outState.putBoolean("ButtonsHidden", true);

        if (mTopBarMode == TopBarMode.Search)
            outState.putBoolean("SearchMode", true);
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        PdfLogUtil.logD("onConfigurationChanged");
        checkScape();
        toggleSuitablePage();
        checkRightSideSeekBar();
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (mSearchTask != null)
            mSearchTask.stop();

        if (mDocKey != null && mDocView != null) {
            SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
            SharedPreferences.Editor edit = prefs.edit();
            edit.putInt("page" + mDocKey, mDocView.getDisplayedViewIndex());
            edit.apply();
        }
    }

    @Override
    public void onDestroy() {
        if(mPdfPageBottomBarFocusHelper != null) {
            mPdfPageBottomBarFocusHelper.setNotifyIdResSelected(null);
            mPdfPageBottomBarFocusHelper = null;
        }
        mHandler.removeCallbacks(hideRunnable);
        clearHandlerWork();
        if(mWeakDocView != null) {
            mWeakDocView.clear();
        }
        if (mDocView != null) {
            mDocView.applyToChildren(new ReaderView.ViewMapper() {
                void applyToView(View view) {
                    ((PageView) view).releaseBitmaps();
                }
            });
        }
        if (core != null) {
            core.onDestroy();
        }
        core = null;
        super.onDestroy();
    }

    private void setButtonEnabled(ImageButton button, boolean enabled) {
        button.setEnabled(enabled);
        button.setColorFilter(enabled ? Color.argb(255, 255, 255, 255) : Color.argb(255, 128, 128, 128));
    }

    private void setLinkHighlight(boolean highlight) {
        mLinkHighlight = highlight;
        // LINK_COLOR tint
        mLinkButton.setColorFilter(highlight ? Color.argb(0xFF, 0x00, 0x66, 0xCC) : Color.argb(0xFF, 255, 255, 255));
        // Inform pages of the change.
        mDocView.setLinksEnabled(highlight);
    }

    private void showButtons() {
        if (core == null)
            return;
        if (!mButtonsVisible) {
            mButtonsVisible = true;
            // 开始关闭倒计时
            resetHideTimer();
            // Update page number text and slider
            int index = mDocView.getDisplayedViewIndex();
            updatePageNumView(index);
            mPageSlider.setMax((core.countPages() - 1) * mPageSliderRes);
            mPageSlider.setProgress(index * mPageSliderRes);
            if (mTopBarMode == TopBarMode.Search) {
                mSearchText.requestFocus();
                showKeyboard();
            }

            Animation anim = new TranslateAnimation(0, 0, -mTopBarSwitcher.getHeight(), 0);
            anim.setDuration(200);
            anim.setAnimationListener(new Animation.AnimationListener() {
                public void onAnimationStart(Animation animation) {
                    mTopBarSwitcher.setVisibility(View.VISIBLE);
                }

                public void onAnimationRepeat(Animation animation) {
                }

                public void onAnimationEnd(Animation animation) {
                }
            });
            mTopBarSwitcher.startAnimation(anim);

            anim = new TranslateAnimation(0, 0, mPageSlider.getHeight(), 0);
            anim.setDuration(200);
            anim.setAnimationListener(new Animation.AnimationListener() {
                public void onAnimationStart(Animation animation) {
                    mPageSlider.setVisibility(View.VISIBLE);
                    mBottomBarView.setVisibility(View.VISIBLE);

                    // 更新焦点状态
                    updateCtrlBtnFocusState(mDocView.mCurrent, true);
                }

                public void onAnimationRepeat(Animation animation) {
                }

                public void onAnimationEnd(Animation animation) {
                    mPageNumberView.setVisibility(View.VISIBLE);
                }
            });
            mPageSlider.startAnimation(anim);
        }
    }

    private void hideButtons() {
        if (mButtonsVisible) {
            mButtonsVisible = false;
            hideKeyboard();

            Animation anim = new TranslateAnimation(0, 0, 0, -mTopBarSwitcher.getHeight());
            anim.setDuration(200);
            anim.setAnimationListener(new Animation.AnimationListener() {
                public void onAnimationStart(Animation animation) {
                }

                public void onAnimationRepeat(Animation animation) {
                }

                public void onAnimationEnd(Animation animation) {
                    mTopBarSwitcher.setVisibility(View.INVISIBLE);
                }
            });
            mTopBarSwitcher.startAnimation(anim);

            anim = new TranslateAnimation(0, 0, 0, mPageSlider.getHeight());
            anim.setDuration(200);
            anim.setAnimationListener(new Animation.AnimationListener() {
                public void onAnimationStart(Animation animation) {
                    mPageNumberView.setVisibility(View.INVISIBLE);
                }

                public void onAnimationRepeat(Animation animation) {
                }

                public void onAnimationEnd(Animation animation) {
                    mPageSlider.setVisibility(View.INVISIBLE);
                    mBottomBarView.setVisibility(View.INVISIBLE);
                }
            });
            mPageSlider.startAnimation(anim);
        }
    }

    // 更新按钮焦点状态
    private void updateCtrlBtnFocusState(int index, boolean simply) {
        PdfLogUtil.logD("[updateCtrlBtnFocusState] index : " + index);
        if(index == 0) {
            checkTurnLeftButtons(false);
            mPageNextBtnContain.requestFocus();
            updateRightSeekbarTint(false);
        } else if(index == mDocView.getAdapter().getCount() - 1) {
            checkTurnRightButtons(false);
            mPagePreviousBtnContain.requestFocus();
            updateRightSeekbarTint(false);
        } else {
            if(simply) {
                mPageNextBtnContain.requestFocus();
                updateRightSeekbarTint(false);
            }
        }
    }

    private void updateRightSeekbarTint(boolean hasFocus) {
        ColorStateList progressTint;
        if(hasFocus) {
            progressTint = ContextCompat.getColorStateList(this, R.color.color_focused);
        } else {
            progressTint = ContextCompat.getColorStateList(this, R.color.color_right_seekbar_normal_tint);
        }
        if(mRightSideSeekBar != null) mRightSideSeekBar.setProgressTintList(progressTint);
        notifySelectedState(hasFocus);
    }

    private void notifySelectedState(boolean isSelected) {
        if(mPdfPageBottomBarFocusHelper != null) {
            int selectedIdRes = mPdfPageBottomBarFocusHelper.findWhichOneSelectedIdResSimple();
            PdfPageBottomBarSelectedHelper.logResId("updateRightSeekbarTint", selectedIdRes);
            if(selectedIdRes != PdfPageBottomBarSelectedHelper.NONE_RES_ID) {
                mPdfPageBottomBarFocusHelper.notifyIdResSelected(selectedIdRes, isSelected);
            }
        }
    }

    private void searchModeOn() {
        if (mTopBarMode != TopBarMode.Search) {
            mTopBarMode = TopBarMode.Search;
            //Focus on EditTextWidget
            mSearchText.requestFocus();
            showKeyboard();
            mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());
        }
    }

    private void searchModeOff() {
        if (mTopBarMode == TopBarMode.Search) {
            mTopBarMode = TopBarMode.Main;
            hideKeyboard();
            mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());
            SearchTaskResult.set(null);
            // Make the ReaderView act on the change to mSearchTaskResult
            // via overridden onChildSetup method.
            mDocView.resetupChildren();
        }
    }

    private void updatePageNumView(int index) {
        if (core == null)
            return;
        mPageNumberView.setText(String.format(Locale.ROOT, "%d/%d", index + 1, core.countPages()));
    }

    private void checkScape() {
        boolean isLandscape = PdfCommUtil.isLandscape(this);
        PdfLogUtil.logD("[checkScape] isLandscape : " + isLandscape);
        mDefaultSuitablePage = isLandscape ? SuitableMode.HOR.name() : SuitableMode.VER.name();
    }

    private void checkRightSideSeekBar() {
        if(mDocView != null && mRightSideSeekBar != null) {
            if(mDocView.isFullPage()) {
                mRightSideSeekBar.setVisibility(View.VISIBLE);
            } else {
                mRightSideSeekBar.setVisibility(View.GONE);
            }
        }
    }

    private void updateRightSeekbarProgress(int total, int page) {
        PdfLogUtil.logD("[updateRightSeekbarProgress] total : " + total + ", page : " + page);
        if(mRightSideSeekBar != null) {
            mRightSideSeekBar.setMax(total <= 0 ? 1 : total);
            mRightSideSeekBar.setProgress(page <= 0 ? 1 : page);
        }
    }

    @SuppressLint("SwitchIntDef")
    private void toggleSuitablePage() {
        PdfLogUtil.logD("mDefaultSuitablePage : " + mDefaultSuitablePage);
        if(SuitableMode.VER.name().equals(mDefaultSuitablePage)) {
            mDefaultSuitablePage = SuitableMode.HOR.name();
            if(mSuitableBtn != null) mSuitableBtn.setBackground(ContextCompat.getDrawable(this, R.drawable.button_suitable_height));
            if(mDocView != null) mDocView.setSuitPageType(ReaderView.SUIT_FOR_WIDTH);
        } else {
            mDefaultSuitablePage = SuitableMode.VER.name();
            if(mSuitableBtn != null) mSuitableBtn.setBackground(ContextCompat.getDrawable(this, R.drawable.button_suitable_width));
            if(mDocView != null) mDocView.setSuitPageType(ReaderView.SUIT_FOR_HEIGHT);
        }
    }

    private final Runnable mAutoTurnPage = new Runnable() {
        @Override
        public void run() {
            if(mWeakDocView.get() != null) {
                if(mWeakDocView.get().mCurrent == mWeakDocView.get().getAdapter().getCount() - 1) {
                    mWeakDocView.get().pushHistory();
                    mWeakDocView.get().setDisplayedViewIndex(0);
                } else {
                    mWeakDocView.get().pushHistory();
                    mWeakDocView.get().moveToNext();
                }
            }
            doHandlerWork();
        }
    };

    /**
     * 触发自动翻页
     * @param isInterfere 是否干扰计时任务，如果是重新计时任务
     */
    private void triggerTurnPageAuto(boolean isInterfere) {
        if(isInterfere) {
            if(currentTurnPageAutoIndex != 0) {
                PdfLogUtil.logD("[triggerTurnPageAuto] 干扰且重新计时");
                // 移除任务
                clearHandlerWork();
                // 重新开始计时任务
                doHandlerWork();
            }
            return;
        }
        // 换挡
        currentTurnPageAutoIndex += 1;
        // 移除任务
        clearHandlerWork();
        // 判断是否回到关闭状态
        if(currentTurnPageAutoIndex == TURN_PAGE_AUTO_TIME.length) {
            PdfLogUtil.logD("切换到: 关闭");
            currentTurnPageAutoIndex = 0;
        } else {
            PdfLogUtil.logD("切换到: " + TURN_PAGE_AUTO_TIME[currentTurnPageAutoIndex].first + "s挡");
            doHandlerWork();
        }
        mTurnPageAutoBtn.setBackground(ContextCompat.getDrawable(this, TURN_PAGE_AUTO_TIME[currentTurnPageAutoIndex].second));
    }

    private void doHandlerWork() {
        // 开始计时任务
        int time = TURN_PAGE_AUTO_TIME[currentTurnPageAutoIndex].first;
        PdfLogUtil.logD("当前: " + time + "s挡");
        if(time > 0) {
            mHandler.postDelayed(mAutoTurnPage, time * 1000L);
        }
    }

    private void clearHandlerWork() {
        mHandler.removeCallbacks(mAutoTurnPage);
    }

    private void checkTurnLeftButtons(boolean isEnable) {
        mPageStartBtn.setEnabled(isEnable);
        mPagePreviousBtn.setEnabled(isEnable);
    }

    private void checkTurnRightButtons(boolean isEnable) {
        mPageEndBtn.setEnabled(isEnable);
        mPageNextBtn.setEnabled(isEnable);
    }

    private void unResSelectedState() {
        if(mSuitableBtn != null) mSuitableBtn.setSelected(false);
        if(mPageStartBtn != null) mPageStartBtn.setSelected(false);
        if(mPageEndBtn != null) mPageEndBtn.setSelected(false);
        if(mPagePreviousBtn != null) mPagePreviousBtn.setSelected(false);
        if(mPageNextBtn != null) mPageNextBtn.setSelected(false);
        if(mTurnPageAutoBtn != null) mTurnPageAutoBtn.setSelected(false);
    }

    private void makeButtonsView() {
        mPdfPageBottomBarFocusHelper = PdfPageBottomBarSelectedHelper.create().setNotifyIdResSelected(new PdfPageBottomBarSelectedHelper.INotifyIdResSelected() {
            @Override
            public void onNotifyIdResSelected(int res, boolean needSelected) {
                PdfPageBottomBarSelectedHelper.logResId("needSelected res", res);
                PdfLogUtil.logD("needSelected : " + needSelected);
                if(needSelected) {
                    if(R.id.btn_suitable_page == res) {
                        if(mSuitableBtn != null) mSuitableBtn.setSelected(true);
                    } else if(R.id.btn_pdf_page_start == res) {
                        if(mPageStartBtn != null) mPageStartBtn.setSelected(true);
                    } else if(R.id.btn_pdf_page_end == res) {
                        if(mPageEndBtn != null) mPageEndBtn.setSelected(true);
                    } else if(R.id.btn_pdf_page_previous == res) {
                        if(mPagePreviousBtn != null) mPagePreviousBtn.setSelected(true);
                    } else if(R.id.btn_pdf_page_next == res) {
                        if(mPageNextBtn != null) mPageNextBtn.setSelected(true);
                    } else if(R.id.btn_turn_page_auto == res) {
                        if(mTurnPageAutoBtn != null) mTurnPageAutoBtn.setSelected(true);
                    }
                } else {
                    unResSelectedState();
                }
            }
        });
        mButtonsView = getLayoutInflater().inflate(R.layout.document_activity, null);
        mDocNameView = mButtonsView.findViewById(R.id.docNameText);
        mPageSlider = mButtonsView.findViewById(R.id.pageSlider);
        mBottomBarView = mButtonsView.findViewById(R.id.pdf_bottom_bar);
        mPageNumberView = mButtonsView.findViewById(R.id.pageNumber);
        mSuitableBtnContain = mButtonsView.findViewById(R.id.btn_suitable_page_contain);
        mSuitableBtn = mButtonsView.findViewById(R.id.btn_suitable_page);
        mSuitableBtnContain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                unResSelectedState();
                toggleSuitablePage();
                checkRightSideSeekBar();
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        // 延迟下，等待测量结束
                        updateRightSeekbarProgress(mDocView.currentScaleTotalPage(), 1);
                    }
                }, 300);
            }
        });
        mSuitableBtnContain.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean hasFocus) {
                mSuitableBtnContain.setBackgroundColor(ContextCompat.getColor(DocumentActivity.this, hasFocus ? R.color.color_focused : android.R.color.transparent));
                mPdfPageBottomBarFocusHelper.updateBtnSelectedState(R.id.btn_suitable_page, hasFocus, view.isEnabled());
            }
        });
        mPageStartBtnContain = mButtonsView.findViewById(R.id.btn_pdf_page_start_contain);
        mPageStartBtn = mButtonsView.findViewById(R.id.btn_pdf_page_start);
        mPageStartBtnContain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(!mPageStartBtn.isEnabled()) return;
                if(mDocView != null) {
                    mDocView.pushHistory();
                    mDocView.setDisplayedViewIndex(0);
                }
            }
        });
        mPageStartBtnContain.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean hasFocus) {
                mPageStartBtnContain.setBackgroundColor(ContextCompat.getColor(DocumentActivity.this, hasFocus ? R.color.color_focused : android.R.color.transparent));
                mPdfPageBottomBarFocusHelper.updateBtnSelectedState(R.id.btn_pdf_page_start, hasFocus, view.isEnabled());
            }
        });
        mPageEndBtnContain = mButtonsView.findViewById(R.id.btn_pdf_page_end_contain);
        mPageEndBtn = mButtonsView.findViewById(R.id.btn_pdf_page_end);
        mPageEndBtnContain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(!mPageEndBtn.isEnabled()) return;
                if(mDocView != null) {
                    mDocView.pushHistory();
                    mDocView.setDisplayedViewIndex(mDocView.getAdapter().getCount() - 1);
                }
            }
        });
        mPageEndBtnContain.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean hasFocus) {
                mPageEndBtnContain.setBackgroundColor(ContextCompat.getColor(DocumentActivity.this, hasFocus ? R.color.color_focused : android.R.color.transparent));
                mPdfPageBottomBarFocusHelper.updateBtnSelectedState(R.id.btn_pdf_page_end, hasFocus, view.isEnabled());
            }
        });
        mPagePreviousBtnContain = mButtonsView.findViewById(R.id.btn_pdf_page_previous_contain);
        mPagePreviousBtn = mButtonsView.findViewById(R.id.btn_pdf_page_previous);
        mPagePreviousBtnContain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(!mPagePreviousBtn.isEnabled()) return;
                if(mDocView != null) {
                    mDocView.pushHistory();
                    mDocView.moveToPrevious();
                }
            }
        });
        mPagePreviousBtnContain.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean hasFocus) {
                mPagePreviousBtnContain.setBackgroundColor(ContextCompat.getColor(DocumentActivity.this, hasFocus ? R.color.color_focused : android.R.color.transparent));
                mPdfPageBottomBarFocusHelper.updateBtnSelectedState(R.id.btn_pdf_page_previous, hasFocus, view.isEnabled());
            }
        });
        mPageNextBtnContain = mButtonsView.findViewById(R.id.btn_pdf_page_next_contain);
        mPageNextBtn = mButtonsView.findViewById(R.id.btn_pdf_page_next);
        mPageNextBtnContain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(!mPageNextBtn.isEnabled()) return;
                if(mDocView != null) {
                    mDocView.pushHistory();
                    mDocView.moveToNext();
                }
            }
        });
        mPageNextBtnContain.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean hasFocus) {
                mPageNextBtnContain.setBackgroundColor(ContextCompat.getColor(DocumentActivity.this, hasFocus ? R.color.color_focused : android.R.color.transparent));
                mPdfPageBottomBarFocusHelper.updateBtnSelectedState(R.id.btn_pdf_page_next, hasFocus, view.isEnabled());
            }
        });
        mTurnPageAutoBtnContain = mButtonsView.findViewById(R.id.btn_turn_page_auto_contain);
        mTurnPageAutoBtn = mButtonsView.findViewById(R.id.btn_turn_page_auto);
        mTurnPageAutoBtnContain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 1页就不需要自动播了
                if(mDocView.getAdapter().getCount() <= 1) return;
                triggerTurnPageAuto(false);
            }
        });
        mTurnPageAutoBtnContain.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean hasFocus) {
                mTurnPageAutoBtnContain.setBackgroundColor(ContextCompat.getColor(DocumentActivity.this, hasFocus ? R.color.color_focused : android.R.color.transparent));
                mPdfPageBottomBarFocusHelper.updateBtnSelectedState(R.id.btn_turn_page_auto, hasFocus, view.isEnabled());
            }
        });
        mSearchButton = mButtonsView.findViewById(R.id.searchButton);
        mOutlineButton = mButtonsView.findViewById(R.id.outlineButton);
        mTopBarSwitcher = mButtonsView.findViewById(R.id.switcher);
        mSearchBack = mButtonsView.findViewById(R.id.searchBack);
        mSearchFwd = mButtonsView.findViewById(R.id.searchForward);
        mSearchClose = mButtonsView.findViewById(R.id.searchClose);
        mSearchText = mButtonsView.findViewById(R.id.searchText);
        mLinkButton = mButtonsView.findViewById(R.id.linkButton);
        mLayoutButton = mButtonsView.findViewById(R.id.layoutButton);
        mTopBarSwitcher.setVisibility(View.INVISIBLE);
        mPageNumberView.setVisibility(View.INVISIBLE);
        mPageSlider.setVisibility(View.INVISIBLE);

        checkScape();
        toggleSuitablePage();
    }

    private void showKeyboard() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null)
            imm.showSoftInput(mSearchText, 0);
    }

    private void hideKeyboard() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null)
            imm.hideSoftInputFromWindow(mSearchText.getWindowToken(), 0);
    }

    private void search(int direction) {
        hideKeyboard();
        int displayPage = mDocView.getDisplayedViewIndex();
        SearchTaskResult r = SearchTaskResult.get();
        int searchPage = r != null ? r.pageNumber : -1;
        mSearchTask.go(mSearchText.getText().toString(), direction, displayPage, searchPage);
    }

    @Override
    public boolean onSearchRequested() {
        if (mButtonsVisible && mTopBarMode == TopBarMode.Search) {
            hideButtons();
        } else {
            showButtons();
            searchModeOn();
        }
        return super.onSearchRequested();
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        if (mButtonsVisible && mTopBarMode != TopBarMode.Search) {
            hideButtons();
        } else {
            showButtons();
            searchModeOff();
        }
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    private final Runnable hideRunnable = new Runnable() {
        @Override
        public void run() {
            hideButtons();  // 隐藏按钮
        }
    };

    // 重置计时器，5 秒后隐藏控件
    private void resetHideTimer() {
        // 每次重置时先移除之前的隐藏任务
        mHandler.removeCallbacks(hideRunnable);
        // 设置 5 秒后隐藏按钮
        mHandler.postDelayed(hideRunnable, 5000);
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        // 获取按键事件的 action 和 keyCode
        int action = event.getAction();
        int keyCode = event.getKeyCode();
        // 全屏状态且遥控器上下
        boolean isSuitableState = SuitableMode.HOR.name().equals(mDefaultSuitablePage) && PdfCommUtil.isLandscape(this)
                || SuitableMode.VER.name().equals(mDefaultSuitablePage) && !PdfCommUtil.isLandscape(this);
        PdfLogUtil.logD("[dispatchKeyEvent] isSuitableState : " + isSuitableState);
        if(isSuitableState) {
            if(keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                if (action == KeyEvent.ACTION_DOWN) {
                } else if (action == KeyEvent.ACTION_UP) {
                    // 向后滚动
                    PdfLogUtil.logD("[dispatchKeyEvent] smartMoveBackwards");
                    if(mDocView != null) mDocView.smartMoveBackwards();
                    updateRightSeekbarTint(true);
                }
            } else if(keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                if (action == KeyEvent.ACTION_DOWN) {
                } else if (action == KeyEvent.ACTION_UP) {
                    // 向前滚动
                    PdfLogUtil.logD("[dispatchKeyEvent] smartMoveForwards");
                    if(mDocView != null) mDocView.smartMoveForwards();
                    updateRightSeekbarTint(true);
                }
            } else if(keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
                if (action == KeyEvent.ACTION_DOWN) {
                } else if (action == KeyEvent.ACTION_UP) {
                    updateRightSeekbarTint(false);
                }
            } else if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
                if (action == KeyEvent.ACTION_DOWN) {
                } else if (action == KeyEvent.ACTION_UP) {
                    updateRightSeekbarTint(false);
                }
            }
        }

        // 如果Bar显示，此时处理焦点控制
        if(mButtonsVisible) {
            // 如果用户按下了遥控器的方向键或 OK 键（可以根据需求添加更多按键）
            if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
                    || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN
                    || keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
                // 用户按下遥控器按键时，重新启动计时器
                resetHideTimer();
            }
        } else
        // 未显示Bar操作左右键可以进行翻页，OK键可以显示Bar
        {
            if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
                if (action == KeyEvent.ACTION_DOWN) {
                    // 左箭头按钮按下, 左箭头按下时的逻辑
                } else if (action == KeyEvent.ACTION_UP) {
                    if(mDocView != null) {
                        mDocView.pushHistory();
                        mDocView.moveToPrevious();
                    }
                }
                return true;  // 消费该事件，不传递给其他控件
            } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
                if (action == KeyEvent.ACTION_DOWN) {
                    // 右箭头按钮按下, 右箭头按下时的逻辑
                } else if (action == KeyEvent.ACTION_UP) {
                    if(mDocView != null) {
                        mDocView.pushHistory();
                        mDocView.moveToNext();
                    }
                }
                return true;  // 消费该事件，不传递给其他控件
            } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
                if (action == KeyEvent.ACTION_DOWN) {
                    // OK键按钮按下, 右箭头按下时的逻辑
                } else if (action == KeyEvent.ACTION_UP) {
                    showButtons();
                }
                return true;  // 消费该事件，不传递给其他控件
            }
        }
        return super.dispatchKeyEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(ev.getAction() == MotionEvent.ACTION_DOWN) {
            mHandler.removeCallbacks(hideRunnable);
        }
        if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) {
            resetHideTimer();
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public void onBackPressed() {
        if (mDocView == null || !mDocView.popHistory()) {
            super.onBackPressed();
            if (mReturnToLibraryActivity) {
                Intent intent = getPackageManager().getLaunchIntentForPackage(getComponentName().getPackageName());
                startActivity(intent);
            }
        }
    }
}
